
#ifndef INTERPRETER_H
#define INTERPRETER_H

#include <cassert>
#include <vector>
#include <string>

#include "Block.h"
#include "Config.h"
#include "Disk.h"
#include "Field.h"
#include "Schema.h"
#include "Relation.h"
#include "MainMemory.h"
#include "SchemaManager.h"
#include "Tuple.h"
#include <queue>

typedef std::vector<std::pair<std::string, std::vector<std::string> > > PushClauseVecType;
typedef std::vector<std::pair<unsigned int, unsigned int> > SublistLimitsType;

typedef struct
{

	std::queue<Tuple> tuples;
	unsigned int blockIdx;
	unsigned int blockEnd;
	unsigned int memBlockIdx;

} Sublist;


class SortFunctor
{

public:

	SortFunctor(const std::string& sortAttrib, const bool intSort)
		: mSortAttrib(sortAttrib), mIntSort(intSort) {}

	bool operator()(const Tuple& lhs, const Tuple& rhs) const
	{
		if(mIntSort == true)
		{
			if(lhs.getField(mSortAttrib).integer < rhs.getField(mSortAttrib).integer)
				return true;

			return false;
		}
		else
		{
			if(*(lhs.getField(mSortAttrib).str) < *(rhs.getField(mSortAttrib).str))
				return true;

			return false;
		}
	}

private:

	std::string mSortAttrib;
	bool mIntSort;
};



class MultiSortFunctor
{

public:

	MultiSortFunctor(const std::vector<std::string>& fieldNames,
						const std::vector<enum FIELD_TYPE>& fieldTypes)
		: mFieldNames(fieldNames), mFieldTypes(fieldTypes) { }

	bool operator()(const Tuple& lhs, const Tuple& rhs) const
	{
		for(int i = 0; i < mFieldTypes.size(); ++i)
		{
			if(mFieldTypes[i] == INT)
			{
				if(lhs.getField(mFieldNames[i]).integer < rhs.getField(mFieldNames[i]).integer)
					return true;
				else if(lhs.getField(mFieldNames[i]).integer > 
												rhs.getField(mFieldNames[i]).integer)
					return false;
			}
			else if(mFieldTypes[i] == STR20)
			{
				if(*(lhs.getField(mFieldNames[i]).str) < *(rhs.getField(mFieldNames[i]).str))
					return true;
				else if(*(lhs.getField(mFieldNames[i]).str) > *(rhs.getField(mFieldNames[i]).str))
					return false;
			}
			else
				assert(false);
		}

		return false;
	}

private:

	std::vector<std::string> mFieldNames;
	std::vector<enum FIELD_TYPE> mFieldTypes;
};


class TupleEqualityFunctor
{

public:

	TupleEqualityFunctor(const std::vector<std::string>& fieldNames,
						const std::vector<enum FIELD_TYPE>& fieldTypes)
		: mFieldNames(fieldNames), mFieldTypes(fieldTypes) { }

	bool operator()(const Tuple& lhs, const Tuple& rhs) const
	{
		for(int i = 0; i < mFieldTypes.size(); ++i)
		{
			if(mFieldTypes[i] == INT)
			{
				if(lhs.getField(mFieldNames[i]).integer != rhs.getField(mFieldNames[i]).integer)
					return false;
			}
			else
			{
				if(lhs.getField(mFieldNames[i]).str->compare(
								*(rhs.getField(mFieldNames[i]).str)) != 0)
					return false;
			}
		}

		return true;
	}

private:

	std::vector<std::string> mFieldNames;
	std::vector<enum FIELD_TYPE> mFieldTypes;
};


class Interpreter
{

public:

	static Interpreter* GetInstance() 
	{ 
		if(mIP == NULL) 
			mIP = new Interpreter(); 
		return mIP; 
	}
	static void Destroy() 
	{ 
		if(mIP != NULL) 
		{
			delete mIP;
			mIP = NULL;
		}
	}
	
	int Interpret(std::string& query);

	unsigned long GetDiskIOs() { return mDisk.getDiskIOs(); }

private:

	Interpreter(): mSchemaManager(&mMem, &mDisk) {};
	
	int HandleCreate();
	int HandleDrop();
	int HandleSelect();
	int HandleDelete();
	int HandleInsert();

	int HandleDistinctAndOrderBy(const std::string& table, const bool distinct, 
		const bool orderby, const std::string& orderbyAttrib,
		const bool projection, const std::vector<std::string>& projectionAttribs,
		std::vector<Tuple>& resultTuples, std::string& resultName);

	int AppendTupleToRelation(Relation* relation_ptr, int memory_block_index, Tuple& tuple);

	int AppendTuplesToRelation(const std::string& tableName, const std::vector<Tuple>& tuples);

	bool CheckWhereOnTuple(const Tuple& tuple, const std::vector<std::string>& whereRPN) const;

	int GetCrossJoinedSchema(const std::vector<std::string>& tableNames, 
			std::vector<std::string>& resultFieldNames, 
			std::vector<enum FIELD_TYPE>& resultFieldTypes);

	std::string GetJoinedName(std::vector<std::string>& tableNames) const;

	int GetNaturalJoinedSchema(const std::vector<std::string>& tableNames, 
			const std::vector<std::string>& joinAttrib,
			std::vector<std::string>& resultFieldNames, 
			std::vector<enum FIELD_TYPE>& resultFieldTypes,
			std::string& resultJoinAttrib);

	int NaturalJoin(std::vector<std::string>& tables, std::vector<std::string>& whereInfix, 
		std::vector<Tuple>& resultTuples, std::string& resultName, 
		std::vector<std::string>& whereRPN, bool& selectionRemains);

	int NaturalJoin3Tables(std::vector<std::string>& tables, 
		std::vector<std::string>& whereInfix, 
		std::vector<Tuple>& resultTuples, std::string& resultName, 
		std::vector<std::string>& whereRPN, bool& selectionRemains);

	int GetNaturalJoinIndicatorClause(std::vector<std::string>& whereInfix,
		std::vector<std::string>& tables,
		std::vector<std::string>& indicator);

	int CrossJoin(std::vector<std::string>& tables, 
		std::vector<std::string>& whereRPN, std::vector<Tuple>& resultTuples,
		std::string& resultName);


	int CreateSortedSublists(const std::string& table, const std::string& sortAttrib,
								std::string& sortedTable, PushClauseVecType& pushClausesRPN,
								SublistLimitsType& sublistLimits);

	int CreateSortedSublists(const std::string& table, const MultiSortFunctor& sorter,
			std::string& sortedTable, SublistLimitsType& sublistLimits);

	int RunNaturalJoinPass2(const std::vector<std::string>& sortedSublists,
		const std::vector<SublistLimitsType>& sublistLimitsVec,
		const std::vector<std::string>& sortAttrib, const std::string& resultTable,
		std::vector<Tuple>& resultTuples);

	int RunDistinctPass2(const std::string& sortedSublists,
		const SublistLimitsType& sublistLimits, const MultiSortFunctor& tupleLess,
		const TupleEqualityFunctor& tupleEquals, const std::string& resultTable,
		std::vector<Tuple>& resultTuples);

	int RunOrderByPass2(const std::string& sortedSublists,
		const SublistLimitsType& sublistLimits, const SortFunctor& tupleLess,
		const std::string& resultTable, std::vector<Tuple>& resultTuples);


	std::vector<std::string> mTokens;

	MainMemory mMem;
	Disk mDisk;
	SchemaManager mSchemaManager;

	static Interpreter* mIP;
};

#endif // INTERPRETER_H
